package com.xr.singleton.controller;

import com.xr.singleton.model.dto.RestResponse;
import com.xr.singleton.model.dto.UserDto;
import com.xr.singleton.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class ElasticSearchController {


    @Autowired
    private RestHighLevelClient client;

    //增加索引
    @RequestMapping("/createIndex")
    public RestResponse createIndex() throws IOException {
        //创建新建索引的请求对象
        CreateIndexRequest request = new CreateIndexRequest("index1");
        //设置分片,副本和映射
        buildSetting(request);
        //设置index的mapping
        buildIndexMapping(request);

        CreateIndexResponse createIndexResponse;
        try {
            createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
            boolean acknowledged = createIndexResponse.isAcknowledged();
            boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();
            if (acknowledged && shardsAcknowledged) {
                log.info("索引创建成功!");
                return RestResponse.ok("索引创建成功!");
            }
        } catch (IOException e) {
            log.error("索引创建失败!", e);
        }

        return RestResponse.ok("索引创建失败!");
    }

    //删除索引
    @RequestMapping("/deleteIndex")
    public RestResponse deleteIndex() throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest("index1");
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
        boolean isSuccessful = delete.isAcknowledged();

        log.info(Boolean.toString(isSuccessful));
        if (isSuccessful) {
            return RestResponse.ok("删除索引成功！");
        }
        return RestResponse.ok("删除索引失败！");

    }

    //返回索引是否存在
    @RequestMapping("/existIndex")
    public RestResponse existIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest("index1");
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        log.info(Boolean.toString(exists));
        if (exists) {
            return RestResponse.ok("索引存在！");
        }
        return RestResponse.ok("索引不存在！");
    }

    //添加文档
    @RequestMapping("/addDocument")
    public RestResponse addDocument() throws IOException {
        UserDto user = new UserDto("zhangsan", "1243");
        IndexRequest request = new IndexRequest("index1").id("1")
                .timeout(TimeValue.timeValueSeconds(1));

        request.source(JsonUtils.obj2Str(user), XContentType.JSON);

        IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);

        System.out.println(indexResponse.toString());
        System.out.println(indexResponse.status());

        return RestResponse.ok("添加文档成功", indexResponse);
    }


    //判断文档是否存在
    @RequestMapping("/isExistDocument")
    public RestResponse isExistDocument() throws IOException {
        GetRequest request = new GetRequest("index1", "1");
        //不获取_source内容,提升效率
        request.fetchSourceContext(new FetchSourceContext(false));
        request.storedFields("_none_");

        boolean exists = client.exists(request, RequestOptions.DEFAULT);
        if (exists) {
            return RestResponse.ok("文档存在！");
        }
        return RestResponse.ok("文档不存在！");
    }

    //更新文档
    @RequestMapping("/updateDocument")
    public RestResponse updateDocument() throws IOException {
        UpdateRequest request = new UpdateRequest("index1", "1");
        request.timeout("1s");

        UserDto user = new UserDto("zhangsan", "456");
        request.doc(JsonUtils.obj2Str(user), XContentType.JSON);
        UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);

        if (RestStatus.OK.equals(updateResponse.status())) {
            return RestResponse.ok("更新文档成功！");
        }
        return RestResponse.ok("更新文档失败！");
    }

    //批量插入数据
    @RequestMapping("/bulkRequest")
    public RestResponse bulkRequest() throws IOException {
        BulkRequest bulkRequest = new BulkRequest().timeout("5s");
        List<UserDto> users = Arrays.asList(new UserDto("test1", "1"), new UserDto("test2", "2"), new UserDto("test3", "3"));

        for (UserDto user : users) {
            bulkRequest.add(new IndexRequest("index1")
                    //.id("xxx")
                    .source(JsonUtils.obj2Str(user), XContentType.JSON));
        }

        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        //是否失败,false表示成功
        System.out.println(bulkResponse.hasFailures());
        System.out.println(bulkResponse.status());

        if (bulkResponse.hasFailures()) {
            return RestResponse.ok("批量插入数据失败！");
        }
        return RestResponse.ok("批量插入数据成功！");

    }

    //查询
    @RequestMapping("/search")
    public RestResponse search() throws IOException {
        SearchRequest request = new SearchRequest("index1");

        //构建搜索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // SearchRequest 搜索请求
        // SearchSourceBuilder 条件构造
        // HighlightBuilder 构建高亮
        // TermQueryBuilder 精确查询
        // MatchAllQueryBuilder .....
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        sourceBuilder.query(matchAllQueryBuilder)
                .timeout(new TimeValue(60, TimeUnit.SECONDS));

        request.source(sourceBuilder);

        SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
        System.out.println(JsonUtils.obj2Str(searchResponse.getHits()));
        System.out.println("===================================");
        for (SearchHit documentFields : searchResponse.getHits()) {
            System.out.println(documentFields.getSourceAsMap());
        }


        return RestResponse.ok("查询成功！", searchResponse.getHits());


    }


    //设置分片
    private void buildSetting(CreateIndexRequest request) {
        request.settings(Settings.builder().put("index.number_of_shards", 3)
                .put("index.number_of_replicas", 2));
    }

    //设置index的mapping
    private void buildIndexMapping(CreateIndexRequest request) {
        Map<String, Object> username = new HashMap<>();
        username.put("type", "text");
        Map<String, Object> password = new HashMap<>();
        password.put("type", "text");

        Map<String, Object> properties = new HashMap<>();
        properties.put("username", username);
        properties.put("password", password);

        Map<String, Object> user = new HashMap<>();
        user.put("properties", properties);

        request.mapping(user);
    }


}
